AWS Lambda & API gateway
使用說明
透過 terraform 檔案來建立 AWS 無伺服器服務 Lambda,並建立 API gateway 來作為終端用戶呼叫 Lambda 函數的方式。在建立 Lambda 服務之前,需要先有欲執行的執行檔,並進一步編譯與包含所需的函式庫。之後執行 terraform 指令來建立 Lambda 與 API gateway 服務。
建置架構

目錄結構
labmda
├── lamda.tf
├── gateway.tf 
├── output.tf 
├── main.go
├── Makefile                       
└── bin                           
     └── main
程式碼與組態檔內容
main.go
  package main
  
  import (
  	"encoding/json"
  	"fmt"
  	"net/http"
  	"time"
  
  	"github.com/aws/aws-lambda-go/events"
  	"github.com/aws/aws-lambda-go/lambda"
  )
  
  // Response of APItype Response struct {
  	Message string `json:"message"`
  	At      string `json:"at"`
  }
  
  func handleRequest(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
  	resp := Response{
  		Message: "Hello World!",
  		At:      time.Now().Format(time.RFC3339),
  	}
  	name, ok := req.QueryStringParameters["name"]
  	if ok {
  		resp.Message = fmt.Sprintf("Hello, %s!\n", name)
  	}
  
  	body, _ := json.Marshal(resp)
  	res := events.APIGatewayProxyResponse{
  		StatusCode: http.StatusOK,
  		Headers:    map[string]string{"Content-Type": "text/json; charset=utf-8"},
  		Body:       string(body),
  	}
  	return res, nil
  }
  
  func main() {
  	lambda.Start(handleRequest)
  }
  
Makefile
  build:
  	GOOS=linux go build -ldflags="-s -w" -o bin/main main.go
  
lambda.tf
  terraform {
    required_providers {
      aws = {
        source = "hashicorp/aws"
      }
    }
  }
  
  provider "aws" {
    profile = "default"
    region  = "ap-northeast-1"
  }
  
  provider "archive" {}
  
  data "archive_file" "zip" {
    type        = "zip"
    source_file = "bin/main"
    output_path = "hello_lambda.zip"
  }
  
  data "aws_iam_policy_document" "policy" {
    statement {
      sid    = ""
      effect = "Allow"
  
      principals {
        identifiers = ["lambda.amazonaws.com"]
        type        = "Service"
      }
  
      actions = ["sts:AssumeRole"]
    }
  }
  
  resource "aws_iam_role" "lambda_exec" {
    name               = "lambda_exec"
    assume_role_policy = data.aws_iam_policy_document.policy.json
  }
  
  resource "aws_lambda_function" "hello" {
    function_name    = "hello_lambda"
    filename         = data.archive_file.zip.output_path
    source_code_hash = data.archive_file.zip.output_base64sha256
  
    role    = aws_iam_role.lambda_exec.arn
    handler = "main"
    runtime = "go1.x"
  }
  
gateway.tf
  resource "aws_api_gateway_rest_api" "hello" {
    name        = "hello"
    description = "Serverless hello world"
  }
  
  resource "aws_api_gateway_resource" "hello" {
    path_part   = "hello"
    parent_id   = aws_api_gateway_rest_api.hello.root_resource_id
    rest_api_id = aws_api_gateway_rest_api.hello.id
  }
  
  resource "aws_api_gateway_method" "hello" {
    rest_api_id   = aws_api_gateway_rest_api.hello.id
    resource_id   = aws_api_gateway_resource.hello.id
    http_method   = "GET"
    authorization = "NONE"
  }
  
  resource "aws_api_gateway_integration" "hello" {
    rest_api_id             = aws_api_gateway_rest_api.hello.id
    resource_id             = aws_api_gateway_resource.hello.id
    http_method             = aws_api_gateway_method.hello.http_method
    integration_http_method = "POST"
    type                    = "AWS_PROXY"
    uri                     = aws_lambda_function.hello.invoke_arn
  }
  
  resource "aws_api_gateway_deployment" "hello_v1" {
    depends_on = [
      aws_api_gateway_integration.hello
    ]
    rest_api_id = aws_api_gateway_rest_api.hello.id
    stage_name  = "v1"
  }
  
  resource "aws_lambda_permission" "hello" {
    statement_id  = "AllowAPIGatewayInvoke"
    action        = "lambda:InvokeFunction"
    function_name = aws_lambda_function.hello.arn
    principal     = "apigateway.amazonaws.com"
  }
  
output.tf
  output "url" {
    value = "${aws_api_gateway_deployment.hello_v1.invoke_url}${aws_api_gateway_resource.hello.path}"
  }
開始建置
- 
先建立資料夾 labmda 並依據程式碼與組態檔內容創建五個文件
 - 
準備編譯 main.go
 - 
執行 terraform init 初始化 Terraform
 - 
執行 terraform apply 建立 Lambda 與 API Gateway
labmda.tf
- 使用 archive_file 打包編譯好的執行檔
 - 使用 aws_iam_policy_document 跟 aws_iam_role 建立 IAM 角色
 - 使用 aws_lambda_function 建立 Lambda 函數
 
gateway.tf
- 使用 aws_api_gateway_rest_api 建立 API 閘道物件
 - 使用 aws_api_gateway_resource 建立 API 閘道資源,設定路徑為 hello
 - 使用 aws_api_gateway_method 建立 API 閘道資源的方法,設定方法為 GET
 - 使用 aws_api_gateway_integration 整合 API 閘道跟 Lambda 函數
- integration_http_method 設定成 POST
 - type 設定成 AWS_PROXY
 - uri 填入 Lambda 函數的 invoke arn
 - 使用 aws_api_gateway_deployment 部署 API 閘道,要設定 depends_on 確保 aws_api_gateway_integration 完成之後才能部署 API
 - 使用 aws_lambda_permission 允許 API 閘道使用 Lambda 函數
 
 
output.tf
- API 的完整網址被輸出到此文件
 - (example) Outputs:測試 url = https://k09e1wi32e.execute-api.ap-northeast-1.amazonaws.com/v1/hello
 
 - 
測試
測試 Lambda
aws lambda invoke --region=ap-northeast-1 --function-name=hello_lambda output.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}檢查輸出資料
cat output.json
{"statusCode":200,"headers":{"Content-Type":"text/json; charset=utf-8"},"multiValueHeaders":null,"body":"{\"message\":\"Hello World!\",\"at\":\"2020-09-26T09:35:12Z\"}"}%測試 API Gateway ( 使用前面 output 的 url )
curl 'https://k09e1wi32e.execute-api.ap-northeast-1.amazonaws.com/v1/hello'
{"message":"Hello World!","at":"2024-01-02T09:44:37Z"}% - 
terraform destroy 刪除所有基礎架構